#pragma once
/**
 * 多人聊天室，服务器端
 * 本多人聊天室是基于UDP接口创建，
 * 实现步骤：初始化socket
 */
#include <cstring>
#include <cerrno>
#include <string>
#include <iostream>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include "BlockQueue.hpp"
#include "Thread.hpp"
#include <unordered_map>

namespace yui
{
    const uint16_t defaultPort = 8888;
    class UdpServer
    {
        public:
            UdpServer(uint16_t port = defaultPort)
                :_port(port)
            {
                pthread_mutex_init(&_mutex,nullptr);

                _producer = new yui::Thread(std::bind(&UdpServer::RecvMessage,this),"Thread-1");
                _consumer = new yui::Thread(std::bind(&UdpServer::BroadcastMessage,this),"Thread-2");
            }
            ~UdpServer()
            {
                _producer->Stop();
                _consumer->Stop();
                pthread_mutex_destroy(&_mutex);
                
                delete _producer;
                delete _consumer;
            }
            void InitServer()
            {
                _sock = socket(AF_INET,SOCK_DGRAM,0);
                if(_sock == -1)
                {
                    perror("socket fail");
                    exit(1);
                }
                //绑定ip和端口号
                struct sockaddr_in local;
                bzero(&local,sizeof(local));
                //填充字段
                local.sin_family = AF_INET;
                local.sin_port = htons(_port);
                local.sin_addr.s_addr = INADDR_ANY;
                if(bind(_sock,(const sockaddr*)&local,sizeof(local)))
                {
                    perror("bind fail");
                    exit(1);
                }
                std::cout<<"bind succeed"<<std::endl;
                _producer->Start();
                _consumer->Start();
            }

            void RecvMessage()
            {
                char buff[4096];
                while(true)
                {
                    struct sockaddr_in peer;
                    socklen_t len = sizeof(peer);
                    ssize_t n = recvfrom(_sock,buff,sizeof(buff)-1,0,(struct sockaddr*)&peer,&len);
                    if(n>0)
                    {
                        buff[n] = '\0';
                    }
                    else
                    {
                        continue;
                    }
                    std::string clientIp = inet_ntoa(peer.sin_addr);
                    uint16_t clientPort = ntohs(peer.sin_port);

                    printf("服务器收到来自[%s:%d]的消息$:%s\n",clientIp.c_str(),clientPort,buff);
                    std::string user = clientIp+"-"+std::to_string(clientPort);
                    pthread_mutex_lock(&_mutex);
                    if(_userTable.count(user)==0)
                    {
                        _userTable[user] = peer;
                    }
                    pthread_mutex_unlock(&_mutex);
                    std::string msg = "["+clientIp+":"+std::to_string(clientPort)+"] say#"+buff;
                    _bq.push(msg);
                }
            }
            void BroadcastMessage()
            {
                while (true)
                {
                    std::string msg;
                    _bq.pop(&msg);
                    std::vector<sockaddr_in> arr;
                    pthread_mutex_lock(&_mutex);
                    for(auto& user:_userTable)
                    {
                        arr.push_back(user.second);
                    }
                    pthread_mutex_unlock(&_mutex);
                    for(auto&addr:arr)
                    {
                        ssize_t n = sendto(_sock,msg.c_str(),msg.size(),0,(const sockaddr*)&addr,sizeof(addr));
                        if(n == -1)
                        {
                            continue;
                        }

                    }
                }
                
            }

            void join() {
                _producer->Join();
                _consumer->Join();
            }


        private:
            uint16_t _port;//端口号
            int _sock;//套接字
            yui::BlockQueue<std::string> _bq;
            std::unordered_map<std::string,struct sockaddr_in> _userTable;
            pthread_mutex_t _mutex;
            yui::Thread* _producer;
            yui::Thread* _consumer;
    };
}
